﻿using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Neat;
using Neat.Components;
using Neat.Mathematics;
using Neat.Graphics;

namespace Sectors
{
    public class WallMesh
    {
        LineSegment edge;
        public string Texture, TextureB;
        float floorHeight;
        float ceilingHeight;
        public Vector2 UV;
        VertexPositionNormalTexture[] vertices;
        static short[] indices = new short[] { 0,1,2, 0, 2, 3 };
        static short[] backwardsIndices = new short[] { 4, 5, 6, 4, 6, 7 };//{ 3, 2, 0, 2, 1, 0 };
        VertexBuffer buffer;
        IndexBuffer indexBuffer, backwardsIndexBuffer;
        NeatGame game;
        public WallMesh(NeatGame _game, LineSegment _edge, string _tex, float _floorHeight, float _ceilingHeight, Vector2 uv)
        {
            game = _game;
            edge = _edge;
            Texture = _tex;
            floorHeight = _floorHeight;
            ceilingHeight = _ceilingHeight;
            UV = uv;
            vertices = CreateVertices(edge, floorHeight, ceilingHeight, UV, game.GetTexture(Texture));
            buffer = new VertexBuffer(game.GraphicsDevice, vertices[0].GetType(), vertices.Length, BufferUsage.WriteOnly);
            buffer.SetData(vertices);
            indexBuffer = new IndexBuffer(game.GraphicsDevice, indices[0].GetType(), indices.Length, BufferUsage.WriteOnly);
            indexBuffer.SetData(indices);
            backwardsIndexBuffer = new IndexBuffer(game.GraphicsDevice, backwardsIndices[0].GetType(), backwardsIndices.Length, BufferUsage.WriteOnly);
            backwardsIndexBuffer.SetData(backwardsIndices);
        }
        
        public static VertexPositionNormalTexture[] CreateVertices(LineSegment edge, float floor, float ceiling, Vector2 uv_shift, Texture2D tx)
        {
            Vector2 d = new Vector2(edge.Length() / tx.Width, Math.Abs(ceiling-floor) / tx.Height);
            var v = new VertexPositionNormalTexture[]{
             new VertexPositionNormalTexture(new Vector3(edge.StartPos.X, floor, -edge.StartPos.Y), Vector3.Forward, Vector2.UnitY),
             new VertexPositionNormalTexture(new Vector3(edge.StartPos.X, ceiling, -edge.StartPos.Y), Vector3.Forward, Vector2.Zero),
             new VertexPositionNormalTexture(new Vector3(edge.EndPos.X, ceiling, -edge.EndPos.Y), Vector3.Forward, Vector2.UnitX),
             new VertexPositionNormalTexture(new Vector3(edge.EndPos.X, floor, -edge.EndPos.Y), Vector3.Forward, Vector2.One),

             new VertexPositionNormalTexture(new Vector3(edge.EndPos.X, floor, -edge.EndPos.Y), Vector3.Forward, Vector2.UnitY),
             new VertexPositionNormalTexture(new Vector3(edge.EndPos.X, ceiling, -edge.EndPos.Y), Vector3.Forward, Vector2.Zero),
             new VertexPositionNormalTexture(new Vector3(edge.StartPos.X, ceiling, -edge.StartPos.Y), Vector3.Forward, Vector2.UnitX),
             new VertexPositionNormalTexture(new Vector3(edge.StartPos.X, floor, -edge.StartPos.Y), Vector3.Forward, Vector2.One)
            };

            for (int i = 0; i < v.Length; i++)
            {
                v[i].TextureCoordinate *= d;
                v[i].TextureCoordinate += uv_shift;
            }
            return v;
        }

        public void Draw(BasicEffect effect)
        {
            effect.GraphicsDevice.SetVertexBuffer(buffer);
            if (!string.IsNullOrEmpty(Texture))
            {
                effect.GraphicsDevice.Indices = indexBuffer;
                effect.Texture = game.GetTexture(Texture);
                foreach (var item in effect.CurrentTechnique.Passes)
                {
                    item.Apply();
                    effect.GraphicsDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList,
                        0, 0, 4, 0, 2);
                }
            }
            if (!string.IsNullOrEmpty(TextureB))
            {
                effect.GraphicsDevice.Indices = backwardsIndexBuffer;
                effect.Texture = game.GetTexture(TextureB);
                foreach (var item in effect.CurrentTechnique.Passes)
                {
                    item.Apply();
                    effect.GraphicsDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList,
                        0, 0, 4, 0, 2);
                }
            }
        }
    }

    public class FloorCeilingMesh
    {
        Polygon polygon;
        VertexPositionNormalTexture[] vertices;
        float floorHeight;
        float ceilingHeight;
        string floorTexture;
        string ceilingTexture;
        Vector2 floorUVShift, ceilingUVShift;
        VertexBuffer buffer;
        NeatGame game;

        public FloorCeilingMesh(NeatGame _game, Polygon _poly, string _floorTex, string _ceilingTex, float _floorHeight, float _ceilingHeight, Vector2 _floorUV, Vector2 _ceilingUV)
        {
            game = _game;
            polygon = new Polygon(_poly);
            floorTexture = _floorTex;
            ceilingTexture = _ceilingTex;
            floorHeight = _floorHeight;
            ceilingHeight = _ceilingHeight;
            floorUVShift = _floorUV;
            ceilingUVShift = _ceilingUV;
            vertices = CreateVertices(polygon, floorHeight, ceilingHeight, floorUVShift, ceilingUVShift, game.GetTexture(floorTexture), game.GetTexture(ceilingTexture));
            buffer = new VertexBuffer(game.GraphicsDevice, vertices[0].GetType(), vertices.Length, BufferUsage.WriteOnly);
            buffer.SetData(vertices);
        }

        public static VertexPositionNormalTexture[] CreateVertices(Polygon poly, float floor, float ceiling, Vector2 floor_uv_shift, Vector2 ceiling_uv_shift, Texture2D floor_tx, Texture2D ceiling_tx)
        {
            poly.Triangulate();
            VertexPositionNormalTexture[] v = new VertexPositionNormalTexture[poly.Triangles.Count*3*2];
            int i=0;
            Vector2 polyMin, polySize;
            poly.GetPositionAndSize(out polyMin, out polySize);


            float l = polySize.Length();
            Vector2 df = new Vector2(polySize.X / floor_tx.Width, polySize.Y / floor_tx.Height);
            Vector2 dc = new Vector2(polySize.X / ceiling_tx.Width, polySize.Y / ceiling_tx.Height);

            foreach (var triangle in poly.Triangles)
                foreach (var vertex in triangle.GetVerticesClockwise())
                    v[i++] = new VertexPositionNormalTexture(new Vector3(vertex.X, floor, -vertex.Y), Vector3.Forward, 
                        ((vertex - polyMin) / polySize) * df + floor_uv_shift);    

            foreach (var triangle in poly.Triangles)
                foreach (var vertex in triangle.GetVerticesCounterClockwise())
                    v[i++] = new VertexPositionNormalTexture(new Vector3(vertex.X, ceiling, -vertex.Y), Vector3.Forward,
                         ((vertex - polyMin) / polySize) * dc + ceiling_uv_shift);

            return v;
        }

        public void Draw(BasicEffect effect)
        {
            effect.GraphicsDevice.SetVertexBuffer(buffer);
            effect.Texture = game.GetTexture(floorTexture);
            foreach (var item in effect.CurrentTechnique.Passes)
            {
                item.Apply();
                effect.GraphicsDevice.DrawPrimitives(PrimitiveType.TriangleList, 0, polygon.Triangles.Count);
            }

            effect.Texture = game.GetTexture(ceilingTexture);
            foreach (var item in effect.CurrentTechnique.Passes)
            {
                item.Apply();
                effect.GraphicsDevice.DrawPrimitives(PrimitiveType.TriangleList, (polygon.Triangles.Count * 3), polygon.Triangles.Count);
            }
        }
    }
}
